﻿using Sprout.Repository.Options;
using Sprout.Repository.Repository;
using Sprout.Core.Extensions;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;

namespace Sprout.Repository
{
    /// <summary>
    /// Data配置
    /// </summary>
    internal sealed class DataSetting
    {
        private static object obj = new object();
        /// <summary>
        /// 数据源
        /// </summary>
        Dictionary<string, ConnectionSetting> DataSources { get; set; }
        /// <summary>
        /// SQL语句
        /// </summary>
        Dictionary<string, TStatement> SqlMaps { get; set; }

        private static DataSetting _instance;
        /// <summary>
        /// 实例
        /// </summary>
        public static DataSetting Instance
        {
            get
            {
                if (_instance != null)
                {
                    return _instance;
                }
                lock (obj)
                {
                    if (_instance != null)
                    {
                        return _instance;
                    }
                    _instance = new DataSetting();
                    return _instance;
                }
            }
        }

        public void Initialize(IServiceCollection services, IList<ConnectionSetting> options)
        {
            InitializeConnectionSettings(options);

            //实例SQL
            InitializeRepository(services);
        }
        /// <summary>
        /// 实例配置
        /// </summary>
        /// <param name="options">配置</param>
        public void InitializeConnectionSettings(IList<ConnectionSetting> options)
        {
            DataSources = new Dictionary<string, ConnectionSetting>();
            foreach (var option in options)
            {
                ConnectionProviderEnum providerType = default(ConnectionProviderEnum);
                switch (option.ProviderName.ToLower())
                {
                    case "mysql":
                        providerType = ConnectionProviderEnum.MySql;
                        break;
                    case "sqlserver":
                        providerType = ConnectionProviderEnum.SqlServer;
                        break;
                    case "oracle":
                        providerType = ConnectionProviderEnum.Oracle;
                        break;
                }
                option.ProviderType = providerType;
                SetConnectionSetting(option);
                DataSources.Add(option.Name.ToLower(), option);
            }
        }
        /// <summary>
        /// 设置address、database
        /// </summary>
        /// <param name="option"></param>
        public void SetConnectionSetting(ConnectionSetting option)
        {
            if (option.ProviderType == ConnectionProviderEnum.MySql)
            {
                var nodes = option.ConnectionString.Split(';');
                foreach (var node in nodes)
                {
                    var keyValue = node.Split('=');
                    if (keyValue[0].ToLower().Trim() == "Server")
                    {
                        option.Address = keyValue[1].Trim();
                    }
                    else if (keyValue[0].ToLower().Trim() == "database")
                    {
                        option.Database = keyValue[1].Trim();
                    }
                }
            }
            else if (option.ProviderType == ConnectionProviderEnum.SqlServer)
            {
                var nodes = option.ConnectionString.Split(';');
                foreach (var node in nodes)
                {
                    var keyValue = node.Split('=');
                    if (keyValue[0].ToLower().Trim() == "data source")
                    {
                        option.Address = keyValue[1].Trim();
                    }
                    else if (keyValue[0].ToLower().Trim() == "database")
                    {
                        option.Database = keyValue[1].Trim();
                    }
                }
            }
            if (option.ProviderType == ConnectionProviderEnum.Oracle)
            {
                int hostIndex = option.ConnectionString.IndexOf("(HOST=") + "(HOST=".Length;
                option.Address = option.ConnectionString.Substring(hostIndex, option.ConnectionString.IndexOf(")", hostIndex) - hostIndex);
                int dataBaseIndex = option.ConnectionString.IndexOf("(SERVICE_NAME=") + "(SERVICE_NAME=".Length;
                option.Database = option.ConnectionString.Substring(dataBaseIndex, option.ConnectionString.IndexOf(")", dataBaseIndex) - dataBaseIndex);
            }
        }

        /// <summary>
        /// 初始化SQL节点
        /// </summary>
        private void InitializeRepository(IServiceCollection services)
        {
            SqlMaps = new Dictionary<string, TStatement>();
            string path = AppContext.BaseDirectory;
            DirectoryInfo directory = new DirectoryInfo(path);
            FileInfo[] files = directory.GetFiles();
            foreach (FileInfo file in files)
            {
                if (file.FullName.EndsWith(".dll") || file.FullName.EndsWith(".exe"))
                {
                    Assembly assembly;
                    try
                    {
                        assembly = Assembly.LoadFrom(file.FullName);
                    }
                    catch
                    {
                        continue;
                    }
                    #region sql节点
                    string[] resourceNames;
                    resourceNames = assembly.GetManifestResourceNames();
                    if (resourceNames != null)
                    {
                        foreach (string name in resourceNames)
                        {
                            if (name.ToLower().EndsWith(".sql.xml"))
                            {
                                Stream stream = assembly.GetManifestResourceStream(name);
                                XmlDocument doc = new XmlDocument();
                                doc.Load(stream);

                                SetXmlDocumentStatement(doc);
                            }
                        }
                    }
                    #endregion

                    #region 动态实现资源仓库接口
                    Type[] ts = null;
                    try
                    {
                        ts = assembly.GetTypes();

                    }
                    catch { }
                    if (ts != null)
                    {
                        var repositorys = ts.Where(n => n.IsInterface && typeof(IRepository).IsAssignableFrom(n) && n != typeof(IRepository));

                        foreach (var repository in repositorys)
                        {
                            services.AddSingleton(repository, RepositoryFacotry.BuildType(repository));
                        }
                    }
                    #endregion
                }
            }
        }
        /// <summary>
        /// 设置SQL节点
        /// </summary>
        /// <param name="doc"></param>
        private void SetXmlDocumentStatement(XmlDocument doc)
        {
            var statementNode = doc.SelectSingleNode("/statements");
            if (statementNode == null)
            {
                return;
            }
            //SQL名称空间
            var sqlNamespace = statementNode.GetAttributes("namespace");
            sqlNamespace = string.IsNullOrEmpty(sqlNamespace) ? string.Empty : $"{sqlNamespace.TrimEnd('.')}.";
            var statementNodes = doc.SelectNodes("/statements/statement");
            if (statementNodes == null || statementNodes.Count == 0)
            {
                return;
            }
            foreach (XmlNode node in statementNodes)
            {
                var id = sqlNamespace + node.GetAttributes("id");
                if (SqlMaps.ContainsKey(id.ToLower()))
                {
                    throw new Exception(string.Format("{0} 已经存在!", id));
                }

                var statementText = node.OuterXml;
                SqlMaps.Add(id.ToLower(), new TStatement()
                {
                    StatementId = id,
                    StatementXml = statementText
                });
            }
        }


        /// <summary>
        /// 获取连接配置
        /// </summary>
        /// <param name="dataSource">数据源</param>
        /// <returns></returns>
        public ConnectionSetting GetConnectionOption(string dataSource)
        {
            if (DataSources == null)
            {
                throw new Exception("数据源未初始化");
            }

            dataSource = GetDataSource(dataSource);

            if (DataSources.ContainsKey(dataSource.ToLower()))
            {
                return DataSources[dataSource.ToLower()];
            }
            else
            {
                throw new Exception("数据源不存在");
            }
        }
        /// <summary>
        /// 获取连接
        /// </summary>
        /// <param name="dataSource">数据源</param>
        /// <returns></returns>
        public IDbConnection GetConnection(string dataSource)
        {
            var option = GetConnectionOption(dataSource);
            switch (option.ProviderType)
            {
                case ConnectionProviderEnum.MySql:
                    return MySqlHelper.GetConnection(option.ConnectionString);
                case ConnectionProviderEnum.SqlServer:
                    return SqlServerHelper.GetConnection(option.ConnectionString);
                case ConnectionProviderEnum.Oracle:
                    return OracleHelper.GetConnection(option.ConnectionString);
                default:
                    throw new Exception("不支持的数据库");
            }
        }
        /// <summary>
        /// 获取数据源
        /// </summary>
        /// <param name="dataSource"></param>
        /// <returns></returns>
        public string GetDataSource(string dataSource)
        {
            if (string.IsNullOrEmpty(dataSource))
            {
                if (DataSources.ContainsKey("default") == false)
                {
                    throw new Exception("必须传入数据源名称");
                }
                else
                {
                    dataSource = "default";
                }
            }
            return dataSource;
        }

        /// <summary>
        /// 是否存在SQL节点信息
        /// </summary>
        /// <param name="statementId"></param>
        /// <returns></returns>
        public bool ExistsTStatement(string statementId)
        {
            return SqlMaps.ContainsKey(statementId.ToLower());
        }

        /// <summary>
        /// 获取节点信息
        /// </summary>
        /// <param name="statementId">节点ID</param>
        /// <returns>节点信息</returns>
        public TStatement GetTStatement(string statementId)
        {
            if (SqlMaps == null)
            {
                throw new Exception("Sql配置文件未初始化");
            }
            if (SqlMaps.ContainsKey(statementId.ToLower()))
            {
                return SqlMaps[statementId.ToLower()];
            }
            throw new Exception($"Sql Xml节点{statementId}不存在");
        }
    }
}
